<!DOCTYPE html>
<html>
<head>
<title>Planogram</title>
<meta name="description" content="An editor for defining planograms: visual displays of merchandise." />
<!-- Copyright 1998-2016 by Northwoods Software Corporation. -->
<meta charset="UTF-8">
<script src="go.js"></script>
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.3/themes/smoothness/jquery-ui.css" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.3/jquery-ui.min.js"></script>
<style type="text/css">
  .ui-accordion .ui-accordion-content {
    padding: 1px;
  }
</style>
<link href="../assets/css/goSamples.css" rel="stylesheet" type="text/css" />  <!-- you don't need to use this -->
<script src="goSamples.js"></script>  <!-- this is only for the GoJS Samples framework -->
<script id="code">
  function init() {
    if (window.goSamples) goSamples();  // init for these samples -- you don't need to call this
    var $ = go.GraphObject.make;

    // Initialize the main Diagram
    var cellSize = new go.Size(50, 50);

    myDiagram =
      $(go.Diagram, "myDiagramDiv",
        {
          grid: $(go.Panel, "Grid",
                  { gridCellSize: cellSize },
                  $(go.Shape, "LineH", { stroke: "lightgray" }),
                  $(go.Shape, "LineV", { stroke: "lightgray" })
                ),
          // support grid snapping when dragging and when resizing
          "draggingTool.isGridSnapEnabled": true,
          "draggingTool.gridSnapCellSpot": go.Spot.Center,
          "resizingTool.isGridSnapEnabled": true,
          allowDrop: true,  // handle drag-and-drop from the Palette
          // For this sample, automatically show the state of the diagram's model on the page
          "ModelChanged": function(e) {
              if (e.isTransactionFinished) {
                document.getElementById("savedModel").textContent = myDiagram.model.toJson();
              }
            },
          "undoManager.isEnabled": true
        });

    // Regular Nodes represent items to be put onto racks.
    // Nodes are currently resizable, but if that is not desired, just set resizable to false.
    myDiagram.nodeTemplate =
      $(go.Node, "Auto",
        {
          resizable: true, resizeObjectName: "SHAPE",
          // because the gridSnapCellSpot is Center, offset the Node's location
          locationSpot: new go.Spot(0, 0, cellSize.width / 2, cellSize.height / 2),
          // provide a visual warning about dropping anything onto an "item"
          mouseDragEnter: function(e, node) {
            e.handled = true;
            node.findObject("SHAPE").fill = "red";
            highlightGroup(node.containingGroup, false);
          },
          mouseDragLeave: function(e, node) {
            node.updateTargetBindings();
          },
          mouseDrop: function(e, node) {  // disallow dropping anything onto an "item"
            node.diagram.currentTool.doCancel();
          }
        },
        // always save/load the point that is the top-left corner of the node, not the location
        new go.Binding("position", "pos", go.Point.parse).makeTwoWay(go.Point.stringify),
        // this is the primary thing people see
        $(go.Shape, "Rectangle",
          { name: "SHAPE",
            fill: "white",
            minSize: cellSize,
            desiredSize: cellSize  // initially 1x1 cell
          },
          new go.Binding("fill", "color"),
          new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify)),
        // with the textual key in the middle
        $(go.TextBlock,
          { alignment: go.Spot.Center, font: 'bold 16px sans-serif' },
          new go.Binding("text", "key"))
      );  // end Node

    // Groups represent racks where items (Nodes) can be placed.
    // Currently they are movable and resizable, but you can change that
    // if you want the racks to remain "fixed".
    // Groups provide feedback when the user drags nodes onto them.

    function highlightGroup(grp, show) {
      if (!grp) return;
      if (show) {  // check that the drop may really happen into the Group
        var tool = grp.diagram.toolManager.draggingTool;
        var map = tool.draggedParts || tool.copiedParts;  // this is a Map
        if (grp.canAddMembers(map.toKeySet())) {
          grp.isHighlighted = true;
          return;
        }
      }
      grp.isHighlighted = false;
    }

    var groupFill = "rgba(128,128,128,0.2)";
    var groupStroke = "gray";
    var dropFill = "rgba(128,255,255,0.2)";
    var dropStroke = "red";

    myDiagram.groupTemplate =
      $(go.Group,
        { resizable: true, resizeObjectName: "SHAPE",
          // because the gridSnapCellSpot is Center, offset the Group's location
          locationSpot: new go.Spot(0, 0, cellSize.width/2, cellSize.height/2)
        },
        // always save/load the point that is the top-left corner of the node, not the location
        new go.Binding("position", "pos", go.Point.parse).makeTwoWay(go.Point.stringify),
        { // what to do when a drag-over or a drag-drop occurs on a Group
          mouseDragEnter: function(e, grp, prev) { highlightGroup(grp, true); },
          mouseDragLeave: function(e, grp, next) { highlightGroup(grp, false); },
          mouseDrop: function(e, grp) {
            var ok = grp.addMembers(grp.diagram.selection, true);
            if (!ok) grp.diagram.currentTool.doCancel();
          },
          handlesDragDropForMembers: true  // don't need to define handlers on member Nodes and Links
        },
        $(go.Shape, "Rectangle",  // the rectangular shape around the members
          { name: "SHAPE",
            fill: groupFill,
            stroke: groupStroke,
            minSize: new go.Size(cellSize.width*2, cellSize.height*2)
          },
          new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify),
          new go.Binding("fill", "isHighlighted", function(h) { return h ? dropFill : groupFill; }).ofObject(),
          new go.Binding("stroke", "isHighlighted", function(h) { return h ? dropStroke: groupStroke; }).ofObject())
      );

    // decide what kinds of Parts can be added to a Group or to top-level
    myDiagram.commandHandler.memberValidation = function(grp, node) {
      if (grp instanceof go.Group && node instanceof go.Group) return false;  // cannot add Groups to Groups
      return true;
    };

    // what to do when a drag-drop occurs in the Diagram's background
    myDiagram.mouseDrop = function(e) {
      // disallow dropping any regular nodes onto the background, but allow dropping "racks"
      var ok = myDiagram.selection.all(function(p) { return p instanceof go.Group; });
      if (!ok) myDiagram.currentTool.doCancel();
    };

    // start off with four "racks" that are positioned next to each other
    myDiagram.model = new go.GraphLinksModel([
      { key: "G1", isGroup: true, pos: "0 0", size: "200 200" },
      { key: "G2", isGroup: true, pos: "200 0", size: "200 200" },
      { key: "G3", isGroup: true, pos: "0 200", size: "200 200" },
      { key: "G4", isGroup: true, pos: "200 200", size: "200 200" }
    ]);
    // this sample does not make use of any links

    jQuery("#accordion").accordion({
      activate: function(event, ui) {
        myPaletteSmall.requestUpdate();
        myPaletteTall.requestUpdate();
        myPaletteWide.requestUpdate();
        myPaletteBig.requestUpdate();
      }
    });

    // initialize the first Palette
    myPaletteSmall =
      $(go.Palette, "myPaletteSmall",
        { // share the templates with the main Diagram
          nodeTemplate: myDiagram.nodeTemplate,
          groupTemplate: myDiagram.groupTemplate,
          layout: $(go.GridLayout)
        });

    var green = '#B2FF59';
    var blue = '#81D4FA';
    var yellow = '#FFEB3B';

    // specify the contents of the Palette
    myPaletteSmall.model = new go.GraphLinksModel([
      { key: "g", color: green },
      { key: "b", color: blue },
      { key: "y", color: yellow }
    ]);

    // initialize the second Palette, of tall items
    myPaletteTall =
      $(go.Palette, "myPaletteTall",
        { // share the templates with the main Diagram
          nodeTemplate: myDiagram.nodeTemplate,
          groupTemplate: myDiagram.groupTemplate,
          layout: $(go.GridLayout)
        });

    // specify the contents of the Palette
    myPaletteTall.model = new go.GraphLinksModel([
      { key: "g", color: green, size: "50 100" },
      { key: "b", color: blue, size: "50 100" },
      { key: "y", color: yellow, size: "50 100" }
    ]);

    // initialize the third Palette, of wide items
    myPaletteWide =
      $(go.Palette, "myPaletteWide",
        { // share the templates with the main Diagram
          nodeTemplate: myDiagram.nodeTemplate,
          groupTemplate: myDiagram.groupTemplate,
          layout: $(go.GridLayout)
        });

    // specify the contents of the Palette
    myPaletteWide.model = new go.GraphLinksModel([
      { key: "g", color: green, size: "100 50" },
      { key: "b", color: blue, size: "100 50" },
      { key: "y", color: yellow, size: "100 50" }
    ]);

    // initialize the fourth Palette, of big items
    myPaletteBig =
      $(go.Palette, "myPaletteBig",
        { // share the templates with the main Diagram
          nodeTemplate: myDiagram.nodeTemplate,
          groupTemplate: myDiagram.groupTemplate,
          layout: $(go.GridLayout)
        });

    // specify the contents of the Palette
    myPaletteBig.model = new go.GraphLinksModel([
      { key: "g", color: green, size: "100 100" },
      { key: "b", color: blue, size: "100 100" },
      { key: "y", color: yellow, size: "100 100" }
    ]);
  }
</script>
</head>
<body onload="init()">
<div id="sample">
  <div style="width:100%; white-space:nowrap;">
    <span style="display: inline-block; vertical-align: top; padding: 5px">
      <div id="accordion">
        <h4>Small items</h4>
        <div>
          <div id="myPaletteSmall" style="width: 140px; height: 360px"></div>
        </div>
        <h4>Tall items</h4>
        <div>
          <div id="myPaletteTall" style="width: 140px; height: 360px"></div>
        </div>
        <h4>Wide items</h4>
        <div>
          <div id="myPaletteWide" style="width: 140px; height: 360px"></div>
        </div>
        <h4>Big items</h4>
        <div>
          <div id="myPaletteBig" style="width: 140px; height: 360px"></div>
        </div>
      </div>
    </span>
    <span style="display: inline-block; vertical-align: top; padding: 5px; width:80%">
      <div id="myDiagramDiv" style="border: solid 1px black; height: 500px"></div>
    </span>
  </div>
  <p>
    A <em>planogram</em> is a visual representation of a store's products or services, often used as a tool to maximize sales.
  </p>
  <p>
    Drag-and-drop "items" from the Palette onto "racks" in the Diagram.
    "Items" are implemented as Nodes; "racks" are implemented as Groups.
    Once items have been placed on a rack, they can be resized, if necessary.
    The <a>DraggingTool.isGridSnapEnabled</a> and <a>ResizingTool.isGridSnapEnabled</a> are both set
    to true to allow for snapping to the background grid.
    Node and Group templates both use dragging functions to allow for highlighting so the user knows which
    rack an item will belong to.
  </p>
  <p>
    A jQuery Accordion is used to create the four collapsible Palettes.
  </p>
  <div>
    Diagram Model saved in JSON format, automatically updated after each transaction:
    <pre id="savedModel" style="height:250px"></pre>
  </div>
  <p>
    See also Northwoods Software's planogramming services: <a href="http://goplanogram.com" target="_blank">GoPlanogram</a>.
  </p>
</div>
</body>
</html>
